//--------------------------------------------------------------------------------------------------
//                                                   ___  __  __  __  __  __  __  __    __  __  ___ 
// Copyright 2010, Koert van der Veer.              / __)(  )(  )(  \/  )(  )(  )(  )  (  )(  )/ __)
// Licensed under the Apache License,              ( (__  )(__)(  )    (  )(__)(  )(__  )(__)( \__ \
// version 2.0; ("the License")                     \___)(______)(_/\/\_)(______)(____)(______)(___/

// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
//     http://www.apache.org/licenses/LICENSE-2.0 
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License. 
//--------------------------------------------------------------------------------------------------
// This file is part of cumulus,  an memory manager implementation in C++.  Details are discussed in
// my blog,  which can be found at http://ondergetekende.nl.  Comments,  suggestions and bug reports
// are welcome. They can be added as comments at the blog. Be advised that in its current form, this
// code is most likely not production ready; it simply hasn't been tested sufficiently. 
//--------------------------------------------------------------------------------------------------
// A memory allocator which just wraps the win32 heaps.
// Two heaps are provided: 
// * Win32ProcessHeap, which wraps the (shared) process heap
// * Win32PrivateHeap, which creates a private heap with custom parameters
// ------------------------------------------------------------------------------------------------

#pragma once

#include <windows.h>

#include "Interface.h"

namespace Cumulus 
{

class Win32HeapAbstractBase : public Interface
{
public:
	// Experiments show that HeapAlloc allocates on 8 byte alignment. This is reasonable, because
	// that's the default alignment for doubles. Also, GlobalAlloc and LocalAlloc have 8-byte
	// alignment guarantees, and those are also based on HeapAlloc.
	// Unfortunately this fact is not guaranteed in the documentation, and might not be true
	// for all platforms.
	static const size_t MinAlignment = 0x08;

protected:
	// Constructor taking a heap
	explicit Win32HeapAbstractBase( HANDLE hInHeap )
		: hHeap( hInHeap )
	{
	}

public:
	void* Malloc( size_t& Size ) 
	{
		void* Result = HeapAlloc( 
			hHeap, // HANDLE hHeap
			0,     // DWORD  dwFlags
			Size );// SIZE_T Size

		// Update Size parameter. (optional)
		// Size = HeapSize( hHeap, 0, Result );

		return Result;
	}

	void Free( void* Memory ) 
	{
		HeapFree( 
			hHeap,   // HANDLE hHeap
			0,       // DWORD  dwFlags
			Memory );// LPVOID lpMem
	}

	size_t GetSize( void* Memory )
	{
		return HeapSize( 
			hHeap, 
			0, 
			Memory );
	}

	bool Resize( void* Memory, size_t& NewSize )
	{
		// Request an in-place resize, fortunately HeapReAlloc provides such a feature
		void* Result = HeapReAlloc( 
			hHeap, 
			HEAP_REALLOC_IN_PLACE_ONLY, // don't try to move the memory
			Memory, 
			NewSize );

		// Check the assumptions from the documentation
		assert( Result == Memory || Result == NULL );

		// Update Size parameter. (optional)
		// NewSize = HeapSize( hHeap, 0, Memory );

		return Result != NULL;
	}

protected:
	// The handle to the heap
	HANDLE hHeap;
};

class Win32ProcessHeap : public Win32HeapAbstractBase
{
public:
	// Win32 heap has optional thread safety, which is enabled for the process heap. While it is 
	// possible to disable thread safety on the process heap, it's not safe to do so, because 
	// multi-threaded code from other sources than cumulus can access that heap.
	static const bool IsThreadSafe = true;

	Win32ProcessHeap()
		: Win32HeapAbstractBase( GetProcessHeap() )
	{
	}
};

template< size_t InitialSize, size_t MaxSize = 0, bool EnableExecute = false >
class Win32PrivateHeap : public Win32HeapAbstractBase
{
public:
	// Win32 heap has optional thread safety.
	// For private heaps, this feature is disabled, because that's a concern for higher layers.
	static const bool IsThreadSafe = false;

public:
	Win32PrivateHeap()
		: Win32HeapAbstractBase( INVALID_HANDLE_VALUE )
	{
		// Disable locking. The higher layers can provide locking if it's required,
		// so we don't need it on this layer.
		DWORD Flags = HEAP_NO_SERIALIZE;

		// Handle optional data execution prevention
		if( EnableExecute )
		{
			Flags |= HEAP_CREATE_ENABLE_EXECUTE;
		}

		hHeap = HeapCreate(
			Flags,
			InitialSize,
			MaxSize
			);
	}

	~Win32PrivateHeap()
	{
		assert( hHeap != INVALID_HANDLE_VALUE );

		HeapDestroy( hHeap );

		// Make sure double destruction is caught
		hHeap = INVALID_HANDLE_VALUE;
	}
};
}